package com.ruiao.car.service;


import com.fasterxml.jackson.core.type.TypeReference;
import com.google.gson.reflect.TypeToken;
import com.ruiao.car.dto.ConfigResDto;
import com.ruiao.car.dto.ResultDto;
import com.ruiao.car.entity.ConfigEntity;
import com.ruiao.car.enums.RedisEnum;
import com.ruiao.car.enums.ResultEnum;
import com.ruiao.car.mapper.ConfigMapper;
import com.ruiao.car.utils.AccountThreadLocal;
import com.ruiao.car.utils.ConstantUtil;
import com.ruiao.car.utils.HeaderUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.thymeleaf.util.ListUtils;

import java.util.*;

@Slf4j
@Service
public class ConfigService {

    @Autowired
    private ConfigMapper configMapper;

    @Autowired
    private RedisService redisService;

    /**
     * 解析请求，匹配规则,支持多个code
     *
     * @param paramCodes
     * @param
     * @return
     */
    public ResultDto<Map<String, Object>> parseHitConfigV2(String paramCodes, String configVersion, String appId, String osType, String appVersion) {
        Map<String, Object> configMap = new HashMap<>();
        try {
            String[] paramCodeArray = paramCodes.split(",");
            for (String paramCode : paramCodeArray) {

                if (!StringUtils.isEmpty(appVersion) && appVersion.indexOf("(") != -1 && appVersion.indexOf(")") != -1) {
                    int startIndex = appVersion.indexOf("(");
                    appVersion = appVersion.substring(0, startIndex);
                }
                List<ConfigResDto> configDtoList = this.queryConfigCache(paramCode, appId, osType,
                        this.versionTransfer(appVersion), Integer.valueOf(configVersion), null);

                if (CollectionUtils.isEmpty(configDtoList)) {
                    configMap.put(paramCode, null);
                    continue;
                }


                int maxConfigVersion = 0;
                List<Object> paramValue = new ArrayList<>();
                for (ConfigResDto configResDto : configDtoList) {
                    int version = configResDto.getConfigVersion();
                    if (version > maxConfigVersion) {
                        maxConfigVersion = version;
                    }
                    paramValue.addAll(configResDto.getParamValue());
                }

                ConfigResDto config = new ConfigResDto();
                config.setConfigVersion(maxConfigVersion);
                config.setParamValue(paramValue);
                configMap.put(paramCode, config);
            }

        } catch (Exception e) {
            log.error("parseHitConfig error :", e);
            return new ResultDto(ResultEnum.SYSTEM_ERROR);
        }
        return new ResultDto(ResultEnum.SUCCESS, configMap);
    }


    /**
     * 校验动态参数
     *
     * @param configDto
     * @return
     * @throws Exception
     */
    private boolean validDynamicParam(ConfigEntity configDto) throws Exception {

        HeaderUtil header = AccountThreadLocal.header();
        Map<String, String> headerMap = BeanUtils.describe(header);
        Map<String, LinkedHashMap<String, Object>> dynamicParam = configDto.getDynamicParam();
        if (dynamicParam != null) {
            for (Map.Entry<String, LinkedHashMap<String, Object>> entry : dynamicParam.entrySet()) {
                String key = entry.getKey();
                String appValue = headerMap.get(key);
                if (StringUtils.isBlank(appValue)) {
                    appValue = "";
                }

                LinkedHashMap<String, Object> innerParam = entry.getValue();
                String operator = innerParam.get("operator").toString();
                if ("in".equals(operator)) {
                    List<String> valueList = (List) innerParam.get("value");
                    if (!valueList.contains(appValue)) {
                        return false;
                    }
                } else {
                    String value = innerParam.get("value").toString();
                    Boolean valueIsDigit = value.matches("^(-?\\d+)(\\.\\d+)?$");
                    double digitValue = 0D;
                    double digitAppValue = 0D;
                    if (valueIsDigit) {
                        digitValue = StringUtils.isBlank(value) ? 0D : Double.parseDouble(value);
                        digitAppValue = StringUtils.isBlank(appValue) ? 0D : Double.parseDouble(appValue);
                    }
                    if (">".equals(operator)) {
                        if (digitAppValue <= digitValue) {
                            return false;
                        }
                    } else if (">=".equals(operator)) {
                        if (digitAppValue < digitValue) {
                            return false;
                        }
                    } else if ("=".equals(operator)) {
                        if (valueIsDigit) {
                            if (digitAppValue != digitValue) {
                                return false;
                            }
                        } else {
                            if (!appValue.equals(value)) {
                                return false;
                            }
                        }
                    } else if ("<".equals(operator)) {
                        if (digitAppValue >= digitValue) {
                            return false;
                        }
                    } else if ("<=".equals(operator)) {
                        if (digitAppValue > digitValue) {
                            return false;
                        }
                    }
                }

            }
        }
        return true;

    }


    public String versionTransfer(String version) {
        if (StringUtils.isBlank(version)) {
            return null;
        }
        String[] versionArray = version.split("\\.");

        Integer first = Integer.parseInt(versionArray[0]);
        String firstStr = String.format("%03d", first);

        Integer second = Integer.parseInt(versionArray[1]);
        String secondStr = String.format("%03d", second);

        Integer third = Integer.parseInt(versionArray[2]);
        String thirdStr = String.format("%04d", third);

        return firstStr + "." + secondStr + "." + thirdStr;
    }

    public String versionToSimple(String version) {
        String[] versionArray = version.split("\\.");
        Integer first = Integer.parseInt(versionArray[0]);
        Integer second = Integer.parseInt(versionArray[1]);
        Integer third = Integer.parseInt(versionArray[2]);
        return first + "." + second + "." + third;
    }


    private List<ConfigResDto> queryConfigCache(String paramCode, String appId, String osType, String transferVersion,
                                                Integer configVersion, String configType) throws Exception {

        String key = RedisEnum.CONFIG_CODE_VAL.getRedisKey(paramCode);
        List<ConfigEntity> configList = redisService.get(key, new TypeReference<List<ConfigEntity>>() {
        });
        if (CollectionUtils.isEmpty(configList)) {
            ConfigEntity param = new ConfigEntity();
            param.setParamCode(paramCode);
            configList = configMapper.queryConfigByCondition(param);
            int expireSecond = ConstantUtil.ONE_MONTH + RandomUtils.nextInt(new Random(60), 1800);
            redisService.set(key, configList, expireSecond);
        }

        List<ConfigResDto> matchList = new ArrayList<>();
        for (ConfigEntity configDto : configList) {

            if (StringUtils.isNotBlank(appId) && !appId.equals(configDto.getAppId())) {
                continue;
            }

            boolean matchOsType = StringUtils.isBlank(osType) || "all".equals(configDto.getOsType()) || osType.equals(configDto.getOsType());
            String minVersion = configDto.getMinVersion();
            String maxVersion = configDto.getMaxVersion();

            boolean matchVersion = StringUtils.isBlank(transferVersion) ||
                    (StringUtils.isBlank(minVersion) || transferVersion.compareTo(minVersion) >= 0)
                            && (StringUtils.isBlank(maxVersion) || transferVersion.compareTo(maxVersion) <= 0);

            if (StringUtils.isNotBlank(configType) && !configType.equals(configDto.getConfigType().name())) {
                continue;
            }

            if (validDynamicParam(configDto) == false) {
                continue;
            }

            if (matchOsType && matchVersion && configVersion <= configDto.getConfigVersion()) {
                matchList.add(new ConfigResDto(configDto));
            }
        }

        return matchList;

    }




}
